#include "StdAfx.h"
#include "DXUT.h"
#include "Mesh.h"

using namespace std;

CMesh::CMesh(const D3DXCOLOR& color)
: m_pVB(NULL)
, m_pIB(NULL)
, m_pSphereTree(NULL)
, m_color(color)
, m_vTranslation(0.f, 0.f, 0.f)
{
}


CMesh::~CMesh(void)
{
	delete m_pSphereTree;
}

void CMesh::LoadFromFile(const wchar_t* szFileName)
{
	wfstream inputFileStream(szFileName, ios_base::in);

	cout.setf(ios_base::floatfield, ios_base::fixed);
	cout.precision(15);

	wchar_t szBuffer[8192];

	for (unsigned int i = 0; i < 3; ++i)
	{
		inputFileStream.getline(szBuffer, 8192);
	}

	unsigned int iNumVertices, iNumFaces;
	std::wstring strTemp;

	for (unsigned int i = 0; i < 2; ++i)
	{
		inputFileStream >> strTemp;
	}
	inputFileStream >> iNumVertices;

	inputFileStream.getline(szBuffer, 8192);
	while (true)
	{
		inputFileStream >> strTemp;
		if (strTemp == L"property")
			inputFileStream.getline(szBuffer, 8192);
		else
			break;
	}

	for (unsigned int i = 0; i < 1; ++i)
	{
		inputFileStream >> strTemp;
	}
	inputFileStream >> iNumFaces;

	for (unsigned int i = 0; i < 3; ++i)
	{
		inputFileStream.getline(szBuffer, 8192);
	}

	DATA_TYPE x, y, z;
	for (unsigned int i = 0; i < iNumVertices; ++i)
	{
		inputFileStream >> x >> y >> z;
		inputFileStream.getline(szBuffer, 8192);
		AddVertices(x, y, z);
	}

	int index[3];
	int iNumVertex;
	for (unsigned int i = 0; i < iNumFaces; ++i)
	{
		inputFileStream >> iNumVertex >> index[0] >> index[1] >> index[2];
		inputFileStream.getline(szBuffer, 8192);
		AddPrimitive(index[0], index[1], index[2]);
	}

	inputFileStream.close();
}

void CMesh::BuildSphereTree()
{
	if (m_pSphereTree)
	{
		m_pSphereTree->Destroy();
		delete m_pSphereTree;
	}
	

	m_pSphereTree = new CSphereTree();
	m_pSphereTree->BuildTree(m_Vertices, m_Primitives, MAX_TREE_LEVEL);
}

void CMesh::AddVertices(DATA_TYPE x, DATA_TYPE y, DATA_TYPE z)
{
	VectorR vector;
	vector[0] = x;
	vector[1] = y;
	vector[2] = z;
	m_Vertices.push_back(vector);
}

void CMesh::AddPrimitive(int index1, int index2, int index3)
{
	CTriangle primitive;
	primitive.index[0] = index1;
	primitive.index[1] = index2;
	primitive.index[2] = index3;
	m_Primitives.push_back(primitive);
}

HRESULT CMesh::InitD3DBuffer(LPDIRECT3DDEVICE9 pDevice9)
{
	SAFE_RELEASE( m_pVB );
	SAFE_RELEASE( m_pIB );

	if( FAILED( pDevice9->CreateVertexBuffer( m_Vertices.size() * sizeof( CUSTOMVERTEX ),
		0, D3DFVF_CUSTOMVERTEX,
		D3DPOOL_DEFAULT, &m_pVB, NULL ) ) )
	{
		return E_FAIL;
	}

	// Fill the vertex buffer. We are algorithmically generating a cylinder
	// here, including the normals, which are used for lighting.
	CUSTOMVERTEX* pVertices;
	if( FAILED( m_pVB->Lock( 0, 0, ( void** )&pVertices, 0 ) ) )
		return E_FAIL;
	for( DWORD i = 0; i < m_Vertices.size(); i++ )
	{
		pVertices[i].position = D3DXVECTOR3( (float)m_Vertices[i][0], (float)m_Vertices[i][1], (float)m_Vertices[i][2] );
	}
	m_pVB->Unlock();

	if( FAILED( pDevice9->CreateIndexBuffer( m_Primitives.size() * 3 * sizeof(DWORD), 0, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &m_pIB, NULL ) ) )
	{
		return E_FAIL;
	}

	DWORD* pIndices;
	if( FAILED( m_pIB->Lock( 0, 0, ( void** )&pIndices, 0 ) ) )
		return E_FAIL;
	for( DWORD i = 0; i < m_Primitives.size(); ++i )
	{
		pIndices[3 * i] = m_Primitives[i].index[0];
		pIndices[3 * i + 1] = m_Primitives[i].index[1];
		pIndices[3 * i + 2] = m_Primitives[i].index[2];
	}
	m_pIB->Unlock();

	if (m_pSphereTree)
		m_pSphereTree->InitD3DBuffer();

	return S_OK;
}

void CMesh::Render(LPDIRECT3DDEVICE9 pDevice9, BOOL bSphere, int iTreeLevel)
{
	D3DMATERIAL9 mtrl;
	ZeroMemory( &mtrl, sizeof( D3DMATERIAL9 ) );
	mtrl.Emissive = m_color;
	pDevice9->SetMaterial( &mtrl );

	if (!bSphere)
	{
		if (m_pVB && m_pIB)
		{
			D3DXMATRIX matWorld;
			D3DXMatrixTranslation(&matWorld, m_vTranslation.x, m_vTranslation.y, m_vTranslation.z);
			DXUTGetD3D9Device()->SetTransform( D3DTS_WORLD, &matWorld );

			pDevice9->SetStreamSource( 0, m_pVB, 0, sizeof( CUSTOMVERTEX ) );
			pDevice9->SetIndices( m_pIB );
			pDevice9->SetFVF( D3DFVF_CUSTOMVERTEX );
			pDevice9->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, m_Vertices.size(), 0, m_Primitives.size() );
		}
	}
	else
	{
		if (m_pSphereTree)
			m_pSphereTree->Render(iTreeLevel, m_vTranslation);
	}

	
}

void CMesh::Destroy()
{
	SAFE_RELEASE(m_pVB);
	SAFE_RELEASE(m_pIB);

	if (m_pSphereTree)
		m_pSphereTree->Destroy();
}

void CMesh::OnLostDevice()
{
	Destroy();
}

void CMesh::OnResetDevice()
{
	InitD3DBuffer(DXUTGetD3D9Device());
}

int CMesh::GetNodeCount(int iLevel) const
{
	if (m_pSphereTree == NULL)
		return NULL;
	return m_pSphereTree->CountNodes(iLevel);
}

void CMesh::FinalizeMesh()
{
	if ( D3DXVec3Length(&m_vTranslation) < EPSILON )
		return;

	for ( unsigned int i = 0; i < m_Vertices.size(); ++i )
	{
		m_Vertices[i][0] += m_vTranslation.x;
		m_Vertices[i][1] += m_vTranslation.y;
		m_Vertices[i][2] += m_vTranslation.z;
	}

	if (m_pSphereTree)
	{
		m_pSphereTree->Translate(m_vTranslation);
	}

	Destroy();
	InitD3DBuffer(DXUTGetD3D9Device());

	m_vTranslation = D3DXVECTOR3(0.f, 0.f, 0.f);
}

void CMesh::GetBounds(DATA_TYPE* pDomainMinOut, DATA_TYPE* pDomainMaxOut)
{
	if (m_Vertices.size() == 0)
		return;

	for (unsigned int j = 0; j < 3; ++j)
	{
		pDomainMinOut[j] = m_Vertices[0][j];
		pDomainMaxOut[j] = m_Vertices[0][j];
	}
	
	for (unsigned int i = 1; i < m_Vertices.size(); ++i)
	{
		for (unsigned int j = 0; j < 3; ++j)
		{
			if (m_Vertices[i][j] < pDomainMinOut[j])
				pDomainMinOut[j] = m_Vertices[i][j];
			if (m_Vertices[i][j] > pDomainMaxOut[j])
				pDomainMaxOut[j] = m_Vertices[i][j];
		}
	}
}